Skip to content

In the last post, I mentioned that the CommonUI Enhanced Input Trigger does not support the "Hold" action. Here is my solution: create a class derived from "CommonBaseButton" to make the Enhanced Input Trigger support the "Hold" action without modifying the UE source code.


HoldSupportBaseButton.h

cpp
#pragma once

#include "CoreMinimal.h"
#include "CommonButtonBase.h"
#include "HoldSupportBaseButton.generated.h"

class FHoldEventDelegate;
/**
 *  Enhanced Input trigger Hold support button
 */
UCLASS()
class AURA_API UHoldSupportBaseButton : public UCommonButtonBase
{
	GENERATED_BODY()

protected:
	virtual void NativeConstruct() override;

	UFUNCTION()
	void EnhancedHoldProgress(float HoldProgress);

public:

	UFUNCTION(BlueprintImplementableEvent, Category = "Aura|UI")
	void OnHoldComplete();

	UFUNCTION()
	void CheckAndAddHoldBinding();

	FDelegateHandle HoldSupportHandle;
};

HoldSupportBaseButton.cpp

cpp
#include "UI/Widget/HoldSupportBaseButton.h"
#include "EnhancedActionKeyMapping.h"
#include "EnhancedInputSubsystems.h"
#include "ICommonInputModule.h"
#include "Input/UIActionBinding.h"
#include "UI/Widget/AuraActivatableWidget.h"

void UHoldSupportBaseButton::NativeConstruct()
{
	Super::NativeConstruct();
	CheckAndAddHoldBinding();
	
	if (GetOwningLocalPlayer())
	{
		UEnhancedInputLocalPlayerSubsystem* EnhancedInputSubsystem = GetOwningLocalPlayer()->GetSubsystem<UEnhancedInputLocalPlayerSubsystem>();
		if (EnhancedInputSubsystem)
		{
			// make sure FEnhancedActionKeyMapping is set up correctly
			EnhancedInputSubsystem->ControlMappingsRebuiltDelegate.AddUniqueDynamic(this, &UHoldSupportBaseButton::CheckAndAddHoldBinding);
		}
	}
	
}

/*
 * I don't want to use bRequiresHold for EnhancedInputAction, I want to use the mapping to determine if it requires hold,
 * the bRequiresHold is for Button itself not for a speacific EnhancedInputAction
 * if a EnhancedInputAction has a hold mapping, then it requires hold.
 * 
 */
void UHoldSupportBaseButton::CheckAndAddHoldBinding()
{
	// if (!bRequiresHold) return;
	if (!TriggeringEnhancedInputAction) return;
	if (!TriggeringBindingHandle.IsValid()) return;
	const TSharedPtr<FUIActionBinding> ActionBinding = FUIActionBinding::FindBinding(TriggeringBindingHandle);
	if (!ActionBinding.IsValid()) return ;
	// ActionBinding->OnHoldActionPressed.AddUObject(this, &UHoldSupportBaseButton::NativeOnPressed);
	ActionBinding->OnHoldActionProgressed.Remove(HoldSupportHandle);
	
	HoldSupportHandle = ActionBinding->OnHoldActionProgressed.AddUObject(this,&UHoldSupportBaseButton::EnhancedHoldProgress);
	if (const UEnhancedInputLocalPlayerSubsystem* InputSystem = GetOwningLocalPlayer()->GetSubsystem<UEnhancedInputLocalPlayerSubsystem>())
	{
		for (const FEnhancedActionKeyMapping& Mapping : InputSystem->GetAllPlayerMappableActionKeyMappings())
		{
			if (Mapping.Action == TriggeringEnhancedInputAction)
			{
				UInputTriggerHold* Trigger = nullptr;
				Mapping.Action->Triggers.FindItemByClass(&Trigger);
				if (Trigger)
				{
					FUIActionKeyMapping KeyMapping(Mapping.Key, Trigger->HoldTimeThreshold, Trigger->HoldTimeThreshold);
					ActionBinding->HoldMappings.Add(KeyMapping);
				}
				else  if (HoldData && bRequiresHold)
				{
					const UCommonUIHoldData* CommonUIHoldBehaviorValues =HoldData.GetDefaultObject();
					FUIActionKeyMapping KeyMapping(Mapping.Key, CommonUIHoldBehaviorValues->KeyboardAndMouse.HoldTime, CommonUIHoldBehaviorValues->KeyboardAndMouse.HoldRollbackTime);
					ActionBinding->HoldMappings.Add(KeyMapping);
				}
			}
		}
	}
}



void UHoldSupportBaseButton::EnhancedHoldProgress(const float HoldProgress)
{
	if (HoldProgress >= 1.0f)
	{
		OnHoldComplete();
	}
}